package com.ktkj.init.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ktkj.init.dto.SysJobCondition;
import com.ktkj.init.dto.SysJobForm;
import com.ktkj.init.dto.SysJobUpdateStatusForm;
import com.ktkj.init.entity.SysJob;
import com.ktkj.init.exception.BusinessException;
import com.ktkj.init.mapper.SysJobMapper;
import com.ktkj.init.service.SysJobService;
import com.ktkj.init.service.UserInfoService;
import com.ktkj.init.task.constants.Constants;
import com.ktkj.init.task.constants.ScheduleConstants;
import com.ktkj.init.task.utils.CronUtils;
import com.ktkj.init.task.utils.ScheduleUtils;
import com.ktkj.init.task.utils.StringUtils;
import com.ktkj.init.utils.SpringUtils;
import com.ktkj.init.vo.QueryResult;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 定时任务调度表 服务实现类
 * </p>
 *
 * @author ktkj
 * @since 2022-07-15
 */
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {

    @Resource
    private Scheduler scheduler;

    @Resource
    private UserInfoService userInfoService;

    /**
     * 项目启动时，初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
    @PostConstruct
    public void init() throws SchedulerException, Exception {
        scheduler.clear();
        List<SysJob> jobList = list();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }

    @Override
    public QueryResult<SysJob> page(SysJobCondition condition) {
        QueryWrapper<SysJob> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(condition.getJobName())) {
            wrapper.like("job_name", condition.getJobName().trim());
        }

        if (StringUtils.isNotBlank(condition.getInvokeTarget())) {
            wrapper.like("invoke_target", condition.getInvokeTarget().trim());
        }

        if (condition.getStatus() != null) {
            wrapper.eq("status", condition.getStatus());
        }

        wrapper.orderByDesc("id");

        List<SysJob> resultData;
        long totalRecord;
        if (condition.getCurrent() != -1) {
            IPage<SysJob> page = page(new Page<>(condition.getCurrent(), condition.getPageSize()), wrapper);
            resultData = page.getRecords();
            totalRecord = page.getTotal();
        } else {
            resultData = list(wrapper);
            totalRecord = resultData.size();
        }

        QueryResult<SysJob> queryResult = new QueryResult<>();
        queryResult.setResultData(resultData);
        queryResult.setTotalRecord(totalRecord);
        return queryResult;
    }

    @Override
    public SysJob saveOrUpdate(SysJobForm form) {
        String errorMsg = "保存失败，请联系管理员！";
        SysJob bean = null;
        try {
            if (!CronUtils.isValid(form.getCronExpression())) {
                throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, "新增任务'" + form.getJobName() + "'失败，Cron表达式不正确");
            } else if (StringUtils.containsIgnoreCase(form.getInvokeTarget(), Constants.LOOKUP_RMI)) {
                throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, "新增任务'" + form.getJobName() + "'失败，目标字符串不允许'rmi'调用");
            } else if (StringUtils.containsAnyIgnoreCase(form.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) {
                throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, "新增任务'" + form.getJobName() + "'失败，目标字符串不允许'ldap(s)'调用");
            } else if (StringUtils.containsAnyIgnoreCase(form.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
                throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, "新增任务'" + form.getJobName() + "'失败，目标字符串不允许'http(s)'调用");
            }

            Long userId = userInfoService.getByUserId();
            if (form.getId() != null) {
                bean = getById(form.getId());
                if (bean == null) {
                    errorMsg = "id错误，没有查询到对应数据！";
                    throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
                }
                bean.setUpdateId(userId);
                bean.setUpdateDate(new Date());

                // 判断是否存在
                JobKey jobKey = ScheduleUtils.getJobKey(form.getId(), ScheduleConstants.GROUP_DEFAULT);
                if (scheduler.checkExists(jobKey)) {
                    // 防止创建时存在数据问题 先移除，然后在执行创建操作
                    scheduler.deleteJob(jobKey);
                }
            } else {
                bean = new SysJob();
                bean.setCreateId(userId);
            }
            SpringUtils.copyPropertiesIgnoreNull(form, bean);
            saveOrUpdate(bean);
            //创建定时任务
            ScheduleUtils.createScheduleJob(scheduler, bean);
        } catch (Exception e) {
            log.error(errorMsg, e);
            throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, errorMsg);
        }
        return bean;
    }

    @Override
    public void batchDelete(List<Long> idList) {
        removeByIds(idList);
        idList.stream().forEach(id -> {
            try {
                //删除定时任务
                scheduler.deleteJob(ScheduleUtils.getJobKey(id, ScheduleConstants.GROUP_DEFAULT));
            } catch (SchedulerException e) {
                log.error("scheduler.deleteJob.error|", e);
            }
        });
    }

    @Override
    public SysJob updateStatus(SysJobUpdateStatusForm form) {
        SysJob bean = getById(form.getId());
        if (bean == null) {
            throw new BusinessException(BusinessException.BUSSINESS_EXCEPTION, "id错误，没有查询到对应数据！");
        }
        bean.setStatus(form.getStatus());
        saveOrUpdate(bean);

        try {
            if (Integer.valueOf(ScheduleConstants.Status.NORMAL.getValue()).equals(form.getStatus())) {
                //恢复定时任务
                scheduler.resumeJob(ScheduleUtils.getJobKey(form.getId(), ScheduleConstants.GROUP_DEFAULT));
            } else if (Integer.valueOf(ScheduleConstants.Status.PAUSE.getValue()).equals(form.getStatus())) {
                //暂停定时任务
                scheduler.pauseJob(ScheduleUtils.getJobKey(form.getId(), ScheduleConstants.GROUP_DEFAULT));
            }
        } catch (SchedulerException e) {
            log.error("scheduler.resumeJob.pauseJob.error|", e);
        }
        return bean;
    }
}
